接着上一篇文章,这一篇我把自己上传到npm上的react-native-segmented-android开发步骤和大家分享。
下载react-native组件命令:1
$ npm install react-native-segmented-android --save
这是效果图:
这次要实现的是View组件,所以要通过继承SimpleViewManager 来实现,步骤和(一)基本保持一致。
开始
Step 1 - 新建react-native工程 ReactNativeSegmentedAndroid1
$ react-native init ReactNativeSegmentedAndroid
Step 2 - 将新建的工程导入android studio然后新建空library(以react-native-segmented-android为library的名称)
Step 3 - 新建空library(以react-native-segmented-android为library的名称)
在library目录下的build.gradle中添加react-native的依赖
1 | // file: android/react-native-segmented-android/build.gradle |
Step 4 - 创建AndroidSegmented类继承SegmentedGroup1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43public class AndroidSegmented extends SegmentedGroup{
public void setSegmentOrientation(String str){
if(str.equals("horizontal")){
setOrientation(RadioGroup.HORIZONTAL);
}else if(str.equals("vertical")){
setOrientation(RadioGroup.VERTICAL);
}
}
public AndroidSegmented(ThemedReactContext context) {
super(context);
setGravity(Gravity.CENTER);
setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
}
private final Runnable mLayoutRunnable = new Runnable() {
public void run() {
measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
}
};
public void requestLayout() {
super.requestLayout();
post(mLayoutRunnable);
}
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
}
}
Step 5 - 继承SimpleViewManager,注意这时就不是继承ReactContextBaseJavaModule了 ,大家可以很明显的发现setChildText()方法上多了一个‘@ReactProp(name = “childText”)’,加上了‘@ReactProp’的,segmented控件多了一个name为childText的属性,值为ReadableArray ( js端代码:childText={[‘One’,’Two’,’Three’,’Four’,”Five”]})。
1 | public class AndroidSegmentedManager extends SimpleViewManager<AndroidSegmented> { |
Step 6 - 创建AndroidSegmentedEvent类继承Event1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public class AndroidSegmentedEvent extends Event<AndroidSegmentedEvent> {
public static final String EVENT_NAME = "topChange";
private final int selectedPosition;
public AndroidSegmentedEvent(int viewId, long timestampMs, int selectedPosition) {
super(viewId, timestampMs);
this.selectedPosition = selectedPosition;
}
public String getEventName() {
return EVENT_NAME;
}
public void dispatch(RCTEventEmitter rctEventEmitter) {
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
}
public short getCoalescingKey() {
return 0;
}
private WritableMap serializeEventData() {
WritableMap eventData = Arguments.createMap();
eventData.putInt("selected", getPosition());
Log.e("AAA","position="+getPosition());
return eventData;
}
private int getPosition() {
return selectedPosition;
}
}
Step 7 - 继承ReactPackage,注意createNativeModules()返回的是加入了 AndroidToastModule 的集合,createJSModules()与createViewManagers()返回的都是空集合,如果Step 4 步继承的是BaseViewManager或其子类,那么createViewManagers()中返回的就是加入了BaseViewManager的集合,其他的就是空集合,一般情况createJSModules()的返回值都是空集合。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class AndroidSegmentedPackage implements ReactPackage {
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(new AndroidSegmentedManager());
}
}
Step 8 - 新建AndroidSegmented.js,文件位置
‘ android/react-native-segmented-android/AndroidSegmented.js ’代码如下,然后在 ‘android/react-native-segmented-android/’下运行如下命令生成package.json文件1
$ npm init //生成package.json文件
1 | //AndroidSegmented.js |
1 | //package.json内容 |
Step 9 - 复制AndroidSegmented.js 文件到‘/ReactNativeSegmentedAndroid/ ’ 目录下,如下是index.android.js代码,然后运行测试
1 | ; |
Install
Step 1 - Install the npm package
1 | $ npm install react-native-degment-android --save |
Step 2 - Update Gradle Settings
1 | // file: android/settings.gradle |
Step 3 - Update app Gradle Build
1 | // file: android/app/build.gradle |
Step 4 - Register React Package
1 | ... |
从react-native的官方文档中我们已经知道facebook的react-native团队已经为我们实现了很多组件,例如 Image、Text、ViewPagerAndroid等,我们在index.android.js中可以直接使用这些组件,这些组件为什么能直接使用呢?
大家会很自然的想到已经封装好了呗。那在哪封装的?如何封装的?其实只要通过命令react-native init ProjectName创建过react-native工程的同学来说,在哪儿封装的一目了然。我们来看react-native工程的结构图:
react-native工程中,在node_modules下有一个很特别的react-native文件夹,android的工程中的build.gradle 文件多了一个依赖,不用想肯定在这两个地方封装的,这也是react-native的关键。1
2
3dependencies {
compile 'com.facebook.react:react-native:0.16.+'
}
首先我们从入口MainActivity开始,看了我的前两篇文章,如何自定义react-native的android组件(一)和(二),要使用一个自定义组件,必须在MainActivity中加入【.addPackage(new AndroidSegmentedPackage()) 】才能使用。
1 | ... |
那么官方的Android组件是如何实现的呢,我们肯定注意到了【.addPackage(new MainReactPackage())】和自定义的是不是很像,格式也一样,我想肯定在这里面有实现,进入MainReactPackage类中,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40//react-native 源码
public class MainReactPackage implements ReactPackage {
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(
new AsyncStorageModule(reactContext),
new FrescoModule(reactContext),
new IntentModule(reactContext),
new LocationModule(reactContext),
new NetworkingModule(reactContext),
new WebSocketModule(reactContext),
new ToastModule(reactContext));
}
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new ReactDrawerLayoutManager(),
new ReactHorizontalScrollViewManager(),
new ReactImageManager(),
new ReactProgressBarViewManager(),
new ReactRawTextManager(),
new ReactScrollViewManager(),
new ReactSwitchManager(),
new ReactTextInputManager(),
new ReactTextViewManager(),
new ReactToolbarManager(),
new ReactViewManager(),
new ReactViewPagerManager(),
new ReactTextInlineImageViewManager(),
new ReactVirtualTextViewManager(),
new SwipeRefreshLayoutManager());
}
}
看了MainReactPackage中的代码,果不其然,首先我们看createViewManagers()方法中的集合,看看集合子集的命名是不是很熟悉,
看看这里一共实现了多少原生控件:DrawerLayout、HorizontalScrollView、HorizontalScrollView、Image等等,还有SwipeRefreshLayout官网上还没有更新这个组件,其实这个版本已经可以使用了。
1 | 1.public class ReactDrawerLayoutManager extends ViewGroupManager<ReactDrawerLayout> |
1 | //贴上一个ReactDrawerLayoutManager源码,大家看看实现 |
原生控件的实现步骤、方法、例子等其实在源码中都有了,想实现什么组件就照着源码开发,绝不会出错啦。
到此只是完成了android端的java代码,那么组件如何与js代码联系起来,并且供js代码调用呢,我们来看看工程中的react-navie文件夹吧,秘密都在它里面。
react-navie文件结构:
打开react-native文件夹,我一眼就注意到了ReactAndroid目录(因为做Android嘛,对含有Android的词比较敏感>_<), 翻遍了其下所有的目录文件,终于找到一个有用点的文件package.json,在其中找到关键的一句话
【”main”: “Libraries/react-native/react-native.js”】图上用红框标注了。
下一步就该看看Libraries目录了,Libraries目录结构:
图上我用红框标注了几个我们熟悉的控件命名的文件家,我们重点关注两个文件夹
Components与CustomComponents 我们看看里面有什么:
红线标注的控件是不是很熟悉,我们随便找一个控件进去看看,就看DrawerAndroid吧,截图如下:
大家遇到的各种不解之处,其实大部分都可以在源码中得到解答,我也在继续学习中,我只是和大家分享我学习的过程,我也只是顺藤摸瓜了解了如何方便的去自定义组件。其实里面的好多ES6语法我也不是特别理解,只是照猫画虎。欢迎大家来吐槽>_<。
1.如何自定义react-native的android组件(一)
3.react-native-0.16.1 自定义Android组件部分的源码初探